<template>
  <geographic-map
    ref="geomap"
    :options="geoConfig"
    @subchartCallback="drawGeoSubChart"
  />
</template>

<script lang="ts">
import { Component, Vue, Prop, Watch } from 'vue-property-decorator'
// @ts-ignore
import { GeographicMap } from '@zjlabvis/vis-charts-plus'
import { cloneDeep } from 'lodash'
import { defaultColors } from '@/components/common/VisualizationComp/constants'

interface IKeyValue {
  [key: string]: any
}

/** 地图不同layer的默认配置 */
const geoLayerDefaultConfig: IKeyValue = {
  point: {
    pointType: 'RoundPoint', // 点的类型
    pointColor: '#ff0000', // 点的颜色
    pointRadius: [3, 5], // 点的半径
  },
  heatmap: {
    heatmapRadius: 10,
  },
  text: {
    textColor: '#0000ff', // 文字颜色
    textFontFamily: 'Arial', // 文字字体
    textFontSize: 18, // 文字字号
  },
  contour: {
    // 等高线
    contourColor: '#ff0000',
    contourSmoothNumber: 2, // 等高平滑次数
    contourThreshold: 1, // 等高拆分步长
    interpolationStep: 500, // 插值步长
    interpolationEffectRadius: 1000, // 插值影响范围
  },
  link: {
    lineColor: '#ff0000',
    lineWidth: [1, 3],
    lineCurverness: 0,
  },
  polygon: {
    borderColor: '#ff0000',
    borderWidth: 1,
    polygonColor: '#ff0000',
  },
  world: {
    borderColor: '#ffffff',
    fillColor: '#ff0000',
    encodingType: 'color', // 编码方式，颜色
    reverseMappingType: 'unk', // 地理编码方式 可选'SOC'和'unk' 默认为'unk'
    selectColor: '#ffff00', // 高亮颜色 默认与填充颜色相同（即默认无高亮）
    subChartTrigger: 'tooltip',
    legendPosition: 'null',
  },
  'country-province': {
    borderColor: '#ffffff',
    encodingType: 'color',
    countryIdType: 'SOC', // 国家代码类型 可选'SOC'和'unk' 默认为'unk'
    selectColor: '#ffff00', // 高亮颜色 默认与填充颜色相同（即默认无高亮）
    reverseMappingType: 'name',
    subChartTrigger: 'tooltip',
    countryId: 'CHN',
    legendPosition: 'null',
  },
  'country-city': {
    borderColor: '#ffffff',
    encodingType: 'color',
    subChartTrigger: 'tooltip',
    selectColor: '#ffff00', // 高亮颜色 默认与填充颜色相同（即默认无高亮）
    reverseMappingType: 'unk', // 市的编码方式 仅选择的国家为中国时有效
    legendPosition: 'null',
  },
  'multi-subchart': {
    subChartWidth: 100, // 子图宽度
    subChartHeight: 75, // 子图高度
  },
}

/**
 * geo多子图配置
 */
const geoSubChartConfig = {
  legendIsShow: false,
  titleIsShow: false,
  tooltipIsShow: false,
  axisTitleIsShow: false,
  xAxisIsShow: false,
  yAxisIsShow: false,
  xAxisGridIsShow: false,
  yAxisGridIsShow: false,
  statistic: {
    title: false,
    content: false,
  },
}

@Component({
  components: {
    GeographicMap,
  },
})
export default class GeoMap extends Vue {
  @Prop({ default: () => ({}) }) config!: IKeyValue
  @Prop({ default: () => ({}) }) chartMapsReal!: IKeyValue

  /** 地图特有的配置 */
  private geoConfig: IKeyValue = {
    ...cloneDeep(this.config),
    mapCenter: [116.397428, 39.90923],
    mapZoom: 10,
    glyphLayers: [],
    activeLayerIndex: 0, // 当前用户正在操作的layer在glyphLayers数组中的索引
  }

  private subCharts: any[] = [] // geo多子图模式拿到的chart实例

  @Watch('config', { deep: true, immediate: true })
  getConfig(newValue: any) {
    this.geoConfig = {
      ...cloneDeep(newValue),
      glyphLayers: cloneDeep(this.geoConfig.glyphLayers),
    }
    this.parseGeoConfig()
  }

  /**
   * 地图组件需要单独添加layer
   */
  parseGeoConfig() {
    if (this.geoConfig.isVisualization) {
      if (this.geoConfig.glyphLayers.length > 0) {
        ;(this.geoConfig.glyphLayers as Array<any>).splice(0, 1)
        this.geoConfig.activeLayerIndex = -1
      }
      this.$nextTick(() => {
        if (this.geoConfig.geoLayer.length > 0) {
          this.geoConfig.glyphLayers.push(this.geoConfig.geoLayer[0])
        }
        this.geoConfig.activeLayerIndex = 0
      })
      return
    }
    const {
      value,
      labelKey,
      valueKey,
      glyphConfig, // glyph 相关的配置
    } = this.geoConfig
    const {
      geoLayer,
      geoCombineField = [],
      encodingType,
      subChartTrigger,
      subChartSize,
      valueSeparator,
      subChartType,
      legendPosition,
      mapTheme,
      mapCenter,
      mapTitle,
    } = glyphConfig || this.geoConfig // || this.geoConfig 是兼容旧图表
    this.geoConfig = {
      ...this.geoConfig,
      mapTheme,
      mapCenter,
      mapTitle,
    }
    if (geoLayer && geoLayer.length > 0 && value.length > 0) {
      // 需要在地图上叠加layer
      const glyphLayers: any = []
      const vKey = this.parseConfigValueKey(valueKey)
      let count = glyphLayers.length
      const coordinateValue =
        geoCombineField.length > 0 ? geoCombineField[0][2] : labelKey[0]
      this.geoConfig.glyphLayers = []
      this.geoConfig.activeLayerIndex = this.geoConfig.glyphLayers.length - 1
      geoLayer.forEach((type: string) => {
        const defaultConfig: any = { ...geoLayerDefaultConfig[type] }
        Object.keys({ ...defaultConfig, ...glyphConfig }).forEach((key) => {
          const target = (glyphConfig || {})[key] || this.geoConfig[key]
          if (target) {
            defaultConfig[key] = target
          }
        })
        switch (type) {
          case 'point':
          case 'heatmap':
          case 'contour':
            glyphLayers.push({
              // 传入geomap组件的各层参数
              type, // 该层类型
              id: count, // 该层id，要求唯一
              config: {
                // 该层的参数，不同类型的layer参数不同，详见wiki
                coordinateValue, // 经纬度列
                encodingValue: vKey, // 用于编码的列
                encodingType: encodingType || 'area', // 编码类型
                value: this.removeGeoEmptyValue(coordinateValue, value), // 该层数据，每一层都能绑定不同数据
                ...defaultConfig,
              },
            })
            count += 1
            break
          case 'text':
            glyphLayers.push({
              // 传入geomap组件的各层参数
              type, // 该层类型
              id: count, // 该层id，要求唯一
              config: {
                // 该层的参数，不同类型的layer参数不同，详见wiki
                coordinateValue, // 经纬度列
                encodingValue: vKey, // 用于编码的列
                encodingType: 'none', // 编码类型
                value: this.removeGeoEmptyValue(
                  coordinateValue,
                  !vKey
                    ? value
                    : value.map((item: any) => {
                        return {
                          ...item,
                          [vKey]: `${item[vKey]}`, // text的时候要转成字符串
                        }
                      })
                ), // 该层数据，每一层都能绑定不同数据
                ...defaultConfig, // default 配置
              },
            })
            count += 1
            break
          case 'line':
          case 'link':
          case 'polygon':
            glyphLayers.push({
              // 传入geomap组件的各层参数
              type, // 该层类型
              id: count, // 该层id，要求唯一
              config: {
                // 该层的参数，不同类型的layer参数不同，详见wiki
                coordinateValue: '$$linePath$$', // 经纬度列
                encodingValue: vKey, // 用于编码的列
                encodingType: vKey ? 'width' : 'none', // 编码类型
                value: this.removeGeoEmptyValue(
                  '$$linePath$$',
                  value.map((item: any) => {
                    return {
                      ...item,
                      // line和polygon 模式下是多个点连接，需要多个坐标点数组集合
                      $$linePath$$:
                        geoCombineField.length > 0
                          ? geoCombineField.map((key: string[]) => [
                              item[key[0]],
                              item[key[1]],
                            ])
                          : (labelKey as string[]).reduce(
                              (accumulator: any[], current) => {
                                accumulator.push(item[current])
                                return accumulator
                              },
                              []
                            ),
                    }
                  })
                ), // 该层数据，每一层都能绑定不同数据
                ...defaultConfig,
              },
            })
            count += 1
            break
          case 'world':
          case 'country-province':
          case 'country-city':
            if (legendPosition !== 'null') {
              defaultConfig.legend = {
                position: legendPosition,
              }
            } else {
              delete defaultConfig.legend
            }
            delete defaultConfig.legendPosition
            glyphLayers.push({
              // 传入geomap组件的各层参数
              type, // 该层类型
              id: count, // 该层id，要求唯一
              config: {
                // 该层的参数，不同类型的layer参数不同，详见wiki
                coordinateValue: labelKey[0], // 经纬度列
                encodingValue: vKey, // 用于编码的列
                encodingType, // 编码类型
                subChartTrigger,
                subChartSize,
                valueSeparator,
                value,
                ...defaultConfig,
                ...glyphConfig,
              },
            })
            count += 1
            break
          case 'multi-subchart': {
            // const legendItems = this.parseSubChartLegendItems(labelKey.slice(-1)[0], value)
            glyphLayers.push({
              // 传入geomap组件的各层参数
              type, // 该层类型
              id: count, // 该层id，要求唯一
              config: {
                // 该层的参数，不同类型的layer参数不同，详见wiki
                coordinateValue: labelKey[0], // 经纬度列
                value: this.removeGeoEmptyValue(labelKey[0], value),
                subChartType,
                legend: {
                  // 多子图的legend需要手动设置 不需要显示legend时不写该项
                  type: 'category', // 图例类型 多子图都为'category'
                  position: 'bottom', // 图例位置 'top','bottom','left','right'
                  field: 'color', // 图例field 一般为'color'
                  marker: {
                    type: 'square', // 图例标记 可选'square' 'circle'
                  },
                  // items: legendItems.length > 10 ? [] : legendItems,
                  handleLegendEvent: this.handleMultiSubChartLegendEvent, // 手动处理图例触发的事件
                },
                ...defaultConfig,
              },
            })
            count += 1
            break
          }
          default:
            break
        }
      })
      // this.geoConfig.glyphLayers = glyphLayers
      glyphLayers.forEach((layers: any) => {
        this.geoConfig.glyphLayers.push(layers)
        this.geoConfig.activeLayerIndex = this.geoConfig.glyphLayers.length - 1
      })
    }
  }

  /**
   * 去除geo数据中 key 为空的值，防止渲染错误
   */
  removeGeoEmptyValue(key: string, value: IKeyValue[]) {
    return value.filter((v) => v[key])
  }

  /**
   * 绘制地图子图，例如line-chart、bar-chart、tooltip等
   */
  private drawGeoSubChart({ subCharts }: any) {
    const {
      glyphLayers,
      labelKey,
      valueKey,
      xAxisTitle,
      value,
    } = this.geoConfig

    // console.log('drawGeoSubChart', subCharts)
    if (glyphLayers[0].config.subChartTrigger === 'tooltip') {
      subCharts.forEach(
        ({ rawData, subChartId }: { rawData: any; subChartId: string }) => {
          const target = this.$el.querySelector(`#${subChartId}`) as HTMLElement
          let v = ''
          if (valueKey[0]) {
            v = valueKey[0].value ? valueKey[0].value : valueKey[0]
          }

          target.innerHTML = `<span>${xAxisTitle || v}</span><br/><span>${
            rawData[labelKey[0]]
          }: ${rawData[v]}</span>`
        }
      )
    }
    // 画geo的多子图
    if (glyphLayers[0].type === 'multi-subchart') {
      this.subCharts = []
      const {
        subChartType,
        subChartWidth,
        subChartHeight,
        legend: { items: legendItems },
      } = glyphLayers[0].config // barChart pieChart
      const v = this.parseConfigValueKey(valueKey)
      const subchartData: any = this.parseGeoMultiSubchartValue(
        {
          labelKey,
          valueKey,
          value,
        },
        legendItems
      )
      subCharts.forEach(
        ({ rawData, subChartId }: { rawData: any; subChartId: string }) => {
          const subChartOptions: any = {
            ...geoSubChartConfig,
            labelKey: labelKey.slice(-1)[0],
            valueKey: v,
            seriesField: labelKey.slice(-1)[0],
            size: [subChartWidth, subChartHeight],
            value: subchartData[rawData[labelKey[0]]],
            tooltipIsShow: true,
            barWidthRatio: 1,
          }

          // eslint-disable-next-line @typescript-eslint/no-unused-vars
          const subchart = new this.chartMapsReal[subChartType](
            subChartId,
            subChartOptions
          )
          // 绑定 'legend-trigger-invisible' 来响应外部legend的交互
          subchart.$on('legend-trigger-invisible', () => null, {
            // 因为chart上不显示legend，需要手动将field（分组的字段）传入
            field: labelKey.slice(-1)[0], // 饼图为labelKey的值
          })

          this.subCharts.push(subchart)
        }
      )
    }
  }

  // 多子图图例交互
  private handleMultiSubChartLegendEvent(eventInfo: any) {
    this.subCharts.forEach((subChart) => {
      subChart.$emit('legend_trigger', {
        trigger: eventInfo.trigger,
        legendItemName: eventInfo.item.key,
      })
    })
  }

  /**
   * geo 多子图模式需要额外处理value格式，分成两部分：坐标点数据和子图数据
   * geo的渲染和数据特殊，需要做一定的约定，如：
   * labelKey是用于计算的geo等相关数据字段名，默认第一个是 经纬度坐标点数据
   * valueKey是基于geo计算出的值的字段名
   */
  parseGeoMultiSubchartValue(
    {
      labelKey,
      valueKey,
      value,
    }: {
      labelKey: string[]
      valueKey: string[]
      value: any[]
    },
    legendItems: Array<{ key: string }>
  ) {
    const [coor] = labelKey
    const extraCols = labelKey.slice(1).concat(valueKey)
    const subChartData: { [key: string]: any[] } = {} // {latlng: [subchart value]}
    value.forEach((v: any) => {
      const data: any = {}
      extraCols.forEach((fieldName: string) => {
        data[fieldName] = v[fieldName]
      })
      if (subChartData[v[coor]]) {
        subChartData[v[coor]].push(data)
      } else {
        subChartData[v[coor]] = [data]
      }
    })

    // 排序
    if (legendItems && legendItems.length > 0) {
      const legendKey = labelKey.slice(-1)[0]
      Object.keys(subChartData).forEach((key) => {
        subChartData[key].sort((a, b) => {
          const indexA = legendItems.findIndex(
            (item) => item.key === a[legendKey]
          )
          const indexB = legendItems.findIndex(
            (item) => item.key === b[legendKey]
          )

          return indexA - indexB
        })
      })
    }
    return subChartData
  }

  /**
   * 解析多子图模式的图例项
   * value chart渲染的value
   */
  private parseSubChartLegendItems(legendKey: string, value: IKeyValue[]) {
    const keys: Set<string> = new Set(value.map((item) => item[legendKey]))
    const colors = defaultColors
    return [...keys].map((key, index) => ({
      value: colors[index % colors.length],
      key,
      status: 'active',
    }))
  }

  /**
   * 解析valueKey
   * 后续需统一成一种格式
   */
  parseConfigValueKey(valueKey: string[] | IKeyValue[]): string {
    if (!Array.isArray(valueKey)) {
      return valueKey
    }
    if (typeof valueKey[0] === 'string') {
      return valueKey[0]
    }
    return valueKey[0] && valueKey[0].value
  }

  public getMapCenter() {
    if (this.$refs.geomap) {
      // @ts-ignore
      return this.$refs.geomap.getMapCenter()
    }
    return null
  }
}
</script>
